package com.gitee.taotaojs.util;

import com.gitee.taotaojs.exception.MyInnerException;
import com.gitee.taotaojs.util.number.NumberFormatUtil;
import com.gitee.taotaojs.util.reflect.ReflectUtil;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.stream.Collectors;

/**
 * 集合工具类
 *
 * @author TaoTaojs
 * @author TaoTaojs
 * @date 2020/7/2 2:23 PM
 * <p>Description</p>
 * 将集合转换为以指定分隔符分隔的字符串【toString()】
 * 将已被固定字符分隔成的字符串拆分为指定类型集合【get***()】
 * 获取集合中复杂对象某个属性并整理为集合【get***()】
 * 比较两个复杂或基本类型集合对结果返回交集【compareToArray***()】
 * 获取集合中重复的对象集合并去重【getRepeat()】
 * 判断Map与Collection对应对象是否为空【isEmpty(), isNotEmpty()】
 * 判断Map与Collection为空就新建的逻辑【isCreate***()】
 * 判断集合/数组中是否存在某个元素
 * 将一组数据固定分组，每组n个元素

 * 关联类:
 * 参考链接：
 * History: <br/>
 * <author>        <time>                      <version>          <desc>
 * TaoTaojs        2020/7/2 2:23 PM                V1.0           支持将集合转换为以分隔符分隔的字符串，获取集合中对象某个不确定类型属性并整理为集合，比较两个复杂或基本类型集合对结果返回交集，获取集合中重复的对象集合并去重
 * TaoTaojs        2020年08月10日10:38:16           V1.0           将一组数据固定分组，每组n个元素
 */

public final class CollectionUtil {

    private CollectionUtil() { }

    /**
     * 将集合转换为逗号分隔的字符串
     * @param list toString()有效的对象集合
     * @param <T> 指定泛型
     * @return 返回逗号分隔的字符串
     */
    public static<T> String toString(List<T> list) {
        if (CollectionUtil.isEmpty(list)) {
            return "";
        }
        return StringUtil.join(list.toArray());
    }


    /**
     * 获取自定义属性并整理成集合
     * @param list 需要被提取属性的对象集合
     * @param clazzV 被提取属性对象的类型
     * @param clazzT 提取到的属性对象的类型
     * @param methodName 提取方法的名称
     * @param <T> 属性的泛型
     * @param <V> 被提取属性对象泛型
     * @return 返回集合列表
     */
    public static<T, V> List<T> getPros(List<V> list, Class<V> clazzV, Class<T> clazzT, String methodName) {
        List<T> reList = new ArrayList<>();
        Method getMethod = ReflectUtil.getGetMethod(clazzV, methodName);
        for (V v : list) {
            try {
                Object pro = getMethod.invoke(v);
                if (pro != null) {
                    reList.add((T) pro);
                }
            } catch (Exception e) {
                throw new MyInnerException(e);
            }
        }
        return reList;
    }
    @SuppressWarnings("checkstyle:JavadocMethod")
    public static<V> List getPros(List<V> list, Class<V> calzz, String methodName) {
        return getPros(list, calzz, Object.class, methodName);
    }

    /**
     * 将字符串转换成Integer集合
     * @param listStr 以指定字符分隔的字符串
     * @param regex 指定字符
     * @return 由字符串转换成的Integer集合
     */
    public static List<Integer> getInteger(String listStr, String regex) {
        if (StringUtil.isBlank(listStr) || StringUtil.isBlank(regex)) {
            return new ArrayList<>();
        }
        return Arrays.asList(listStr.split(regex)).stream()
                .filter(str -> !str.isEmpty())
                .map(NumberFormatUtil::stringToInteger)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    /**
     * 将字符串转换为string集合
     * @param listStr 以指定字符分隔的字符串
     * @param regex 指定字符
     * @return 由字符串转换成的String集合
     */
    public static List<String> getString(String listStr, String regex) {
        if (StringUtil.isBlank(listStr) || StringUtil.isBlank(regex)) {
            return new ArrayList<>();
        }
        return Arrays.asList(listStr.split(regex)).stream()
                .filter(str -> !str.isEmpty())
                .collect(Collectors.toList());
    }

    /**
     * 将字符串转换为数字集合（双精度集合）
     * @param listStr 以指定字符分隔的字符串
     * @param regex 指定字符
     * @return 由字符串转成成的Double集合
     */
    public static List<Double> getDouble(String listStr, String regex) {
        if (StringUtil.isBlank(listStr) || StringUtil.isBlank(regex)) {
            return new ArrayList<>();
        }
        return Arrays.asList(listStr.split(regex)).stream()
                .filter(str -> !str.isEmpty())
                .map(NumberFormatUtil::stringToDouble)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    @SuppressWarnings("checkstyle:JavadocMethod")
    public static<T> List<Integer> getIds(List<T> list, Class<T> clazz){
        return getPros(list, clazz, "id");
    }
    @SuppressWarnings("checkstyle:JavadocMethod")
    public static<T> List<Integer> getIds(List<T> list, Class<T> clazz, String methodName){
        return getPros(list, clazz, methodName);
    }
    @SuppressWarnings("checkstyle:JavadocMethod")
    public static List<Integer> getIds(String listStr){
        return getInteger(listStr, ",");
    }

    @SuppressWarnings("checkstyle:JavadocMethod")
    public static List<Integer> getInteger(String listStr){
        return getInteger(listStr, ",");
    }

    @SuppressWarnings("checkstyle:JavadocMethod")
    public static<T> List<String> getString(List<T> list, Class<T> clazz, String methodName){
        return getPros(list, clazz, methodName);
    }
    @SuppressWarnings("checkstyle:JavadocMethod")
    public static List<String> getString(String listStr){
        return getString(listStr, ",");
    }
    @SuppressWarnings("checkstyle:JavadocMethod")
    public static<T> List<Integer> getNumber(List<T> list, Class<T> clazz, String methodName){
        return getPros(list, clazz, methodName);
    }
    @SuppressWarnings("checkstyle:JavadocMethod")
    public static List<Double> getNumber(String listStr){
        return getNumber(listStr);
    }

    /**
     * 排除集合中的空值
     * @param coll 集合
     * @param <T> 集合泛型
     */
    public static <T> void toNotEmpty(Collection<T> coll) {
        Iterator<T> it = coll.iterator();
        while (it.hasNext()) {
            T x = it.next();
            if (x == null) {
                it.remove();
            }
        }
    }

    /**
     * 比较两个集合，找出其中的交集，并返回（找到过程中会删除原来两个集合中的数据引用） - 使用equals判断
     * @param clazz 对象类型
     * @param listDb DB - 返回一个需要删除的列表
     * @param listVO VO - 返回一个需要添加的列表
     * @param keyName 主键属性字段/关键属性字段
     * @param <T> 对象类型
     * @return 返回两个集合的交集 - 返回一个需要修改的列表
     */
    public static<T> List<T> compareToArray(Class<T> clazz, List<T> listDb, List<T> listVO, String keyName){
        List<T> list = new ArrayList<>();
        if(listDb == null || listVO == null) {
            return list;
        }

        toNotEmpty(listDb);
        toNotEmpty(listVO);

        Method getKeyMethod = ReflectUtil.getGetMethod(clazz, keyName);
        Method setKeyMethod = ReflectUtil.getSetMethod(clazz, keyName, getKeyMethod.getReturnType());

        Iterator<T> iterator1 = listDb.iterator();

        while(iterator1.hasNext()){
            T item1 = iterator1.next();
            Iterator<T> iterator2 = listVO.iterator();
            while(iterator2.hasNext()){
                T item2 = iterator2.next();
                if(compareToArrayReturnJudge(getKeyMethod, setKeyMethod, item1, item2)){
                    iterator1.remove();
                    iterator2.remove();
                    list.add(item2);
                    break;
                }
            }
        }

        return list;
    }
    @SuppressWarnings("checkstyle:JavadocMethod")
    public static<T> List<T> compareToArray(Class<T> clazz, List<T> listDb, List<T> listVO){
        return compareToArray(clazz, listDb, listVO, "id");
    }

    /**
     * 比较两个集合并获取交集方法的主要处理方法（为了降低代码复杂度，特拆开）
     * @param getKeyMethod 获取key的方法对象
     * @param setKeyMethod 设置key的方法对象
     * @param item1 对象1
     * @param item2 对象2
     * @param <T> 对象类类型
     * @return 是否相同
     */
    private static<T> boolean compareToArrayReturnJudge(Method getKeyMethod, Method setKeyMethod, T item1, T item2){
        Object key1 = ReflectUtil.invokeMethod(item1, getKeyMethod);
        Object key2 = ReflectUtil.invokeMethod(item2, getKeyMethod);
        if (key1 != null && key2 != null && key1.equals(key2)) {
            //如果两个对象都有key值，且key值相同
            return true;
        } else if (key1 != null && key2 == null && item1.equals(item2)){
            //如果VO没有key值，且两个对象equals相同
            ReflectUtil.invokeMethod(item2, setKeyMethod, key1);
            return true;
        } else if (key1 == null && key2 != null && item1.equals(item2)){
            //如果DB没有key值，且两个对象equals相同
            return true;
        }
        return false;
    }

    /**
     * 对比基本类型集合的方法（直接对比equals方法）
     * @param list1 基本类型集合1
     * @param list2 基本类型集合2
     * @param <T> 基本类型泛型
     * @return 交集集合
     */
    public static<T> List<T> compareToArrayBasic(List<T> list1, List<T> list2){
        List<T> list = new ArrayList<>();
        if(list1 == null || list2 == null) {
            return list;
        }

        Iterator<T> iterator1 = list1.iterator();
        while(iterator1.hasNext()) {
            T item1 = iterator1.next();
            Iterator<T> iterator2 = list2.iterator();
            while (iterator2.hasNext()) {
                T item2 = iterator2.next();
                if(item1.equals(item2)){
                    iterator1.remove();
                    iterator2.remove();
                    list.add(item2);
                    break;
                }
            }
        }
        return list;
    }

    /**
     * 获取一个集合中重复的对象并返回为一个集合
     * @param data 集合
     * @param <T> 集合泛型
     * @return 集合中的重复对象
     */
    public static<T> List<T> getRepeat(List<T> data){
        List<T> list = new ArrayList<>();

        Iterator<T> iterator1 = data.iterator();
        while(iterator1.hasNext()) {
            T item1 = iterator1.next();
            Iterator<T> iterator2 = data.iterator();
            while (iterator2.hasNext()) {
                T item2 = iterator2.next();
                if(item1.equals(item2)){
                    iterator2.remove();
                    list.add(item2);
                    break;
                }
            }
        }

        return list;
    }

    /**
     * 判断Collection对象与Map对象是否为空
     * @param collection 集合
     * @return 如果为空则返回对应的数据
     */
    @SuppressWarnings("checkstyle:JavadocMethod")
    public static boolean isEmpty(Collection<?> collection) {
        return (collection == null || collection.isEmpty());
    }
    @SuppressWarnings("checkstyle:JavadocMethod")
    public static boolean isNotEmpty(Collection<?> collection) {
        return (collection != null && !collection.isEmpty());
    }
    @SuppressWarnings("checkstyle:JavadocMethod")
    public static<K, V> boolean isEmpty(Map<K, V> map){
        return map == null || map.size() == 0;
    }
    @SuppressWarnings("checkstyle:JavadocMethod")
    public static<K, V> boolean isNotEmpty(Map<K, V> map){
        return map != null && map.size() > 0;
    }
    @SuppressWarnings("checkstyle:JavadocMethod")
    public static boolean isEmpty(Object[] collection) {
        return (collection == null || collection.length == 0);
    }
    @SuppressWarnings("checkstyle:JavadocMethod")
    public static boolean isNotEmpty(Object[] collection) {
        return (collection != null && collection.length > 0);
    }


    /**
     * 如果Map的key对应的value为空，则根据类型
     * @param map map集合
     * @param key 指定主键值
     * @param clazz value类型
     */
    public static void isCreateMap(Map map, Object key, Class<?> clazz){
        Object obj = map.get(key);
        if(obj == null){
            if(clazz.equals(List.class)) {
                map.put(key, new ArrayList<>());
            }else if(clazz.equals(ArrayList.class)){
                map.put(key, new ArrayList<>());
            }else if(clazz.equals(LinkedList.class)){
                map.put(key, new LinkedList<>());
            }else if(clazz.equals(Set.class)){
                map.put(key, new HashSet<>());
            }else if(clazz.equals(LinkedHashSet.class)){
                map.put(key, new LinkedHashSet<>());
            }else if(clazz.equals(TreeSet.class)){
                map.put(key, new TreeSet<>());
            }else if(clazz.equals(Map.class)){
                map.put(key, new HashMap<>(ConstantUtil.TWO));
            }else if(clazz.equals(TreeMap.class)){
                map.put(key, new TreeMap<>());
            }else if(clazz.equals(LinkedHashMap.class)){
                map.put(key, new LinkedHashMap<>());
            }
        }

    }

    /**
     * 如果集合为空，则返回对应实例
     * @param collection 集合
     * @param <T> 集合泛型
     * @return 如果集合为空，则返回对应实例
     */
    public static<T> List<T> isCreateList(List<T> collection){
        if(collection == null) {
            collection = new ArrayList<>();
        }
        return collection;
    }

    /**
     * 如果集合为空，则返回对应实例
     * @param collection 集合
     * @param <T> 集合泛型
     * @return 如果集合为空，则返回对应实例
     */
    public static<T> Set<T> isCreateSet(Set<T> collection){
        if(collection == null) {
            collection = new HashSet<>();
        }
        return collection;
    }

    /**
     * 判断集合/数组中是否存在某个元素
     * @param objs 数组
     * @param obj 对应的元素，需要查找的元素
     * @param clazz 元素类型
     * @param pros 用于判断的属性
     * @param <T> 元素泛型
     * @return 如果pros没传，则直接查询，如果有pros，则使用对应属性比较
     */
    public static<T> boolean isExist(T[] objs, T obj, Class<T> clazz, String... pros){
        if(objs == null){
            return false;
        }
        List<T> objList = new ArrayList<>();
        objList.addAll(Arrays.asList(objs));

        if(isEmpty(pros)){
            return objList.contains(obj);
        }else{
            for (String proStr : pros) {
                T pro = ReflectUtil.invokeMethod(obj, proStr);
                List<T> proList = getPros(objList, clazz, proStr);
                if(proList.contains(pro)){
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 将一组数据固定分组，每组n个元素
     * @param source 要分组的数据源
     * @param n      每组n个元素
     * @param <T> 元素泛型
     * @return 一个二维数组
     */
    public static <T> List<List<T>> fixedGrouping(List<T> source, int n) {

        if (null == source || source.isEmpty() || n <= 0) {
            return Collections.emptyList();
        }
        List<List<T>> result = new ArrayList<>();
        int remainder = source.size() % n;
        int size = (source.size() / n);
        for (int i = 0; i < size; i++) {
            List<T> subset = null;
            subset = source.subList(i * n, (i + 1) * n);
            result.add(subset);
        }
        if (remainder > 0) {
            List<T> subset = null;
            subset = source.subList(size * n, size * n + remainder);
            result.add(subset);
        }
        return result;
    }

    /**
     * 将一组数据固定分组，每组n个元素
     * @param basicMap Map类型的数据，key必须为Integer类型
     * @param n 每组几个
     * @param sum 总长度，如果超出总长度将会抛弃
     * @param <T> 泛型
     * @return 一个二维数组
     */
    public static<T> List<List<T>> fixedGrouping(Map<Integer, T> basicMap, int n, int sum) {
        Map<Integer, T> sourceMap = new HashMap<>(basicMap);
        List<Integer> source = new ArrayList<>(sourceMap.keySet());
        if (source.isEmpty() || n <= 0) {
            return Collections.emptyList();
        }
        List<List<T>> result = new ArrayList<>();
        List<T> subset = new ArrayList<>();
        for (int i = 1; i <= sum; i++) {
            T temp = sourceMap.get(i);
            if(temp != null){
                subset.add(temp);
                sourceMap.remove(i);
            }
            if((i) % n == 0) {
                result.add(new ArrayList<>(subset));
                subset.clear();
            }
        }

        return result;
    }


    /**
     * 将集合对象整理成Map结构
     * @param clazzK keyClass
     * @param list 集合
     * @param key key对象
     * @param <K> 泛型
     * @param <T> 泛型
     * @return Map结构的数据
     */
    public static<K, T> Map<K, T> toMap(Class<K> clazzK, Collection<T> list, String key){
        if(isEmpty(list)){
            return new HashMap<>();
        }
        Map<K, T> map = new HashMap<>(2);
        Method getMethod = ReflectUtil.getGetMethod(clazzK, key);
        for (T t : list) {
            K k = (K) ReflectUtil.invokeMethod(t, getMethod);
            map.put(k, t);
        }
        return map;
    }
    @SuppressWarnings("checkstyle:JavadocMethod")
    public static<T> Map<Integer, T> toMap(Collection<T> list){
        return toMap(Integer.class, list, "id");
    }

    /**
     * list高效去重
     * @param list list集合
     * @param <T> 泛型
     */
    public static<T> void removeDuplicate(List<T> list){
        if(isEmpty(list)){
            return;
        }
        LinkedHashSet<T> set = new LinkedHashSet<>(list.size());
        set.addAll(list);
        list.clear();
        list.addAll(set);
    }

    /**
     * 以某个属性名称为key，将集合整理为Map对象（默认属性名为id）
     * @param clazz 类类型
     * @param list 集合
     * @param proName 属性名
     * @param <K> 属性名类型泛型
     * @param <V> 集合泛型
     * @return map对象
     */
    public static<K, V> Map<K, V> objList2Map(Class<V> clazz, List<V> list, String proName){
        Map<K, V> map = new HashMap<>(2);
        Method getIdMethod = ReflectUtil.getGetMethod(clazz, proName);
        for (V v : list) {
            try {
                map.put((K) getIdMethod.invoke(v), v);
            } catch (Exception e) {
                throw new MyInnerException(e);
            }
        }
        return map;
    }
    @SuppressWarnings("checkstyle:MissingJavadocMethod")
    public static<T> Map<Integer, T> objList2Map(Class<T> clazz, List<T> list){
        return objList2Map(clazz, list, "id");
    }

}
